package com.sdhs.file.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.sdhs.common.core.domain.CommonIdListDTO;
import com.sdhs.common.core.domain.R;
import com.sdhs.common.utils.SecurityUtils;
import com.sdhs.common.utils.file.FileUtils;
import com.sdhs.file.domain.SysFileDTO;
import com.sdhs.file.domain.SysFileEntity;
import com.sdhs.file.domain.SysFileVO;
import com.sdhs.file.service.ISysFileService;
import com.sdhs.file.utils.MinioTemplate;

import cn.hutool.core.bean.BeanUtil;
import io.minio.messages.ListPartsResult;
import io.minio.messages.Part;

/**
 * 文件请求处理
 *
 * @author ruoyi
 */
@RestController
public class SysFileController {
    private static final Logger log = LoggerFactory.getLogger(SysFileController.class);

    @Autowired
    private ISysFileService sysFileService;

    @Autowired
    private MinioTemplate minioTemplate;

//    @Autowired
//    private MinioConfig minioConfig;
    
//    @Autowired
//    private CephConfig cephConfig;
    
//    @PostMapping("upload_test")
//    public R<SysFile> upload_test(MultipartFile file) {
//    	sysFileService.upload_test(file, null, null, null);
//    	return R.ok();
//    }
    
//    @GetMapping("/download_test")
//    public void download_test(@RequestParam(name = "id") String id, HttpServletResponse response) {
//        sysFileService.download_test(id, response);
//    }
    
    /**
     * 获取文件下载URL，有效期5分钟
     * @param id
     * @return
     */
//    @GetMapping("/generatePresignedUrl")
//    public R<String> generatePresignedUrl(@RequestParam(name = "id") String id) {
//    	String url = sysFileService.generatePresignedUrl(id, cephConfig.getExpirationInSeconds());
//    	return R.ok(url);
//    }
    
    /**
     * 获取文件下载URL，永久有效
     * @param id
     * @return
     */
//    @GetMapping("/generatePresignedUrlForever")
//    public R<String> generatePresignedUrlForever(@RequestParam(name = "id") String id) {
//    	String url = sysFileService.generatePresignedUrl(id, -1);
//    	return R.ok(url);
//    }

    /**
     * 文件上传请求
     */
    @PostMapping("upload")
    public R<SysFileVO> upload(MultipartFile file) {
        try {
            // 上传并返回访问地址
            SysFileEntity sysFileEntity = sysFileService.uploadFile(file, null, null, null);
            SysFileVO sysFile = new SysFileVO();
            sysFile.setFileSuffix("." + sysFileEntity.getFileType());
            sysFile.setOssId(String.valueOf(sysFileEntity.getId()));
            sysFile.setName(FileUtils.getName(sysFileEntity.getUrl()));
            sysFile.setUrl(sysFileEntity.getUrl());
            sysFile.setFileSize(sysFileEntity.getFileSize());
            sysFile.setCreateBy(SecurityUtils.getUsername());
            sysFile.setOriginalName(file.getOriginalFilename());
            sysFile.setCreateName(SecurityUtils.getNickName());
            sysFile.setCreateTime(new Date());
            return R.ok(sysFile);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            return R.fail(e.getMessage());
        }
    }

    /**
     * 文件下载
     *
     * @param id 文件oss_id
     * @return
     */
    @GetMapping("/download")
    public void download(@RequestParam(name = "id") Long id, HttpServletResponse response) {
        sysFileService.download(id, response);
    }

    /**
     * 批量文件打包下载
     *
     * @param dto 文件ossId数组
     * @return
     */
    @PostMapping("/downloadByFileIds")
    public void downloadByFileIds(@Validated CommonIdListDTO dto, HttpServletResponse response) {
        sysFileService.downloadByFileIds(dto.getIdList(), response);
    }

    /**
     * 文件远程上传
     *
     * @return
     */
//    @ApiOperation("文件远程上传")
//    @PostMapping("/uploadByUrl")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "fileUrl", value = "文件下载url", required = true),
//            @ApiImplicitParam(name = "folder", value = "文件夹", required = false),
//            @ApiImplicitParam(name = "fileName", value = "文件全名，要带文件类型,例如：test.jpg", required = true),
//    })
//    public AjaxResult uploadByUrl(@RequestParam("fileUrl") String fileUrl, @RequestParam(name = "folder", required = false) String folder, @RequestParam(name = "fileName") String fileName) {
//        log.info("uploadByUrl-->>fileUrl: {}, folder: {}, fileName: {}", fileUrl, folder, fileName);
//        if (StringUtils.isBlank(fileUrl)) {
//            return AjaxResult.error("文件url为空", 500);
//        }
//        SysFileEntity sysFileEntity = sysFileService.uploadFileByUrl(fileUrl, folder, fileName);
//        return AjaxResult.success(sysFileEntity);
//    }

    /**
     * 文件分享
     *
     * @return
     */
//    @GetMapping("/shareFile")
//    public AjaxResult shareFile(@RequestParam(name = "id") String id, @RequestParam(name = "expiry") Integer expiry) {
//        String url = sysFileService.shareFile(id, expiry);
//        return AjaxResult.success(url);
//    }

    /**
     * 根据id查询附件对象
     *
     * @param id
     * @return
     */
    @GetMapping("queryById")
    public R<SysFileEntity> queryById(@RequestParam("id") Long id) {
        SysFileEntity sysFileEntity = sysFileService.selectSysFileById(id);
        return R.ok(sysFileEntity);
    }

    @GetMapping("queryDTOById")
    public R<SysFileDTO> queryDTOById(@RequestParam("id") Long id) {
        SysFileEntity sysFileEntity = sysFileService.selectSysFileById(id);
        SysFileDTO dto = BeanUtil.toBean(sysFileEntity, SysFileDTO.class);
        return R.ok(dto);
    }

    /**
     * 创建文件夹
     *
     * @param bucketName
     * @return
     */
    @GetMapping("makeBucket")
    public R<Void> makeBucket(@RequestParam("bucketName") String bucketName) {
        sysFileService.makeBucket(bucketName);
        return R.ok(null, "创建成功");
    }


    /**
     * 拷贝文件
     *
     * @param bucketName
     * @param objectName
     * @param destBucketName
     * @param destObjectName
     * @return
     */
    @GetMapping("copyObject")
    public R<Void> copyObject(@RequestParam("bucketName") String bucketName, @RequestParam("objectName") String objectName,
                              @RequestParam("destBucketName") String destBucketName, @RequestParam("destObjectName") String destObjectName) {
        sysFileService.copyObject(bucketName, objectName, destBucketName, destObjectName);
        return R.ok(null, "拷贝成功");
    }


    /**
     * 拷贝文件
     *
     * @param bucketName
     * @param objectName
     * @param destBucketName
     * @param destObjectName
     * @return
     */
    @GetMapping("copyObjectNew")
    public R<String> copyObjectNew(@RequestParam("bucketName") String bucketName, @RequestParam("objectName") String objectName,
                              @RequestParam("destBucketName") String destBucketName, @RequestParam("destObjectName") String destObjectName) {
        String url = sysFileService.copyObjectNew(bucketName, objectName, destBucketName, destObjectName);
        return R.ok(url, "拷贝成功");
    }
    /**
     * 返回分片上传需要的签名数据URL及 uploadId
     *
     * @param bucketName 上传文件夹
     * @param fileName   上传文件原始名称
     * @param chunkSize  分片数量
     * @return
     */
    @GetMapping("/createMultipartUpload")
    public R<Map<String, Object>> createMultipartUpload(String bucketName, String fileName, Integer chunkSize) throws Exception {
        if (org.apache.commons.lang3.StringUtils.isBlank(bucketName)) {
            bucketName = "default";
        }
        Map<String, Object> result = new HashMap<>();
        // 1. 获取uploadId
        String uploadId = minioTemplate.uploadId(bucketName, null, fileName, null, null);
        result.put("uploadId", uploadId);
        Map<String, String> reqParams = new HashMap<>();
        reqParams.put("uploadId", uploadId);
        // 4. 循环分块数 从1开始
        for (int i = 1; i <= chunkSize; i++) {
            reqParams.put("partNumber", String.valueOf(i));
            // 获取URL
            String uploadUrl = minioTemplate.getPresignedObjectUrl(bucketName, fileName, reqParams);
            // 添加到集合
            result.put("chunk_" + (i - 1), uploadUrl);
        }
        result.put("totalNumber", chunkSize);
        return R.ok(result);
    }

    /**
     * 分片上传完后合并
     *
     * @param fileName    存储文件名称
     * @param uploadId    上传ID
     * @param md5         文件md5值
     * @param originName  原文件名称
     * @param totalNumber 分片总量
     * @return /
     */
//    @GetMapping("/completeMultipartUpload")
//    public R<SysFileEntity> completeMultipartUpload(String bucketName, String fileName, String uploadId, String md5, String originName, Integer totalNumber) {
//        if (org.apache.commons.lang3.StringUtils.isBlank(bucketName)) {
//            bucketName = minioTemplate.getConfig().getBucketName();
//        }
//        try {
//            Part[] parts = new Part[totalNumber];
//            ListPartsResult partResult = minioTemplate.listParts(bucketName, fileName, totalNumber, 0, uploadId);
//            int partNumber = 0;
//            for (Part part : partResult.partList()) {
//                parts[partNumber] = new Part(partNumber + 1, part.etag());
//                partNumber++;
//            }
//
//            minioTemplate.completeMultipartUpload(bucketName, fileName, uploadId, parts);
//            String fileUrl = minioTemplate.getObjectUrl(bucketName, fileName);
//
//            String fileType = FilenameUtils.getExtension(fileName);
//            String name = FilenameUtils.getBaseName(fileName);
//            SysFileEntity sysFileEntity;
//            LoginUser loginUser = SecurityUtils.getLoginUser();
//            sysFileEntity = SysFileEntity.builder()
//                    .id(name)
//                    .fileFolder(bucketName)
//                    .fileName(originName)
//                    .fileType(fileType)
//                    .url(fileUrl)
//                    .userId(loginUser.getUserid())
//                    .userName(loginUser.getUsername())
//                    .md5(md5)
//                    .build();
//            // 保存bean
//            sysFileService.insertSysFile(sysFileEntity);
//            // TODO 需要发送消息告知，待完成
///*            auditClient.sendMessage(
//                    new MessageDTO()
//                            .setResult(StrUtil.format("文件:[{}]已经上传完毕!", originName))
//                            .setType("upload")
//                            .setUserId(userDetail.getId().toString()));*/
//            return R.ok(sysFileEntity);
//        } catch (Exception e) {
//            e.printStackTrace();
//            return R.fail(500, "文件合并错误");
//        }
//    }

    /**
     * 查看已完成得任务,用于断点续传
     *
     * @param uploadId
     * @param bucketName
     * @param fileName
     * @return
     */
    @GetMapping("/taskInfo")
    public R<List<Integer>> taskInfo(@RequestParam String uploadId, @RequestParam String bucketName, @RequestParam String fileName) {
        ListPartsResult partResult;
        List<Integer> result = new ArrayList<>();
        try {
            partResult = minioTemplate.listParts(bucketName, fileName, 1000, 0, uploadId);
        } catch (Exception e) {
            e.printStackTrace();
            return R.ok(result);
        }
        // 返回结果格式转换
        result = partResult.partList().stream().map(Part::partNumber).collect(Collectors.toList());
        log.info("partResult:{}", result);
        return R.ok(result);
    }

    /**
     * 根据md5获取文件
     *
     * @param md5
     * @return
     */
    @GetMapping("/getFileByMd5")
    public R getFileByMd5(@RequestParam String md5, @RequestParam String fileName) {
        return sysFileService.getFileByMd5(md5, fileName);
    }

    /**
     * 分片上传完后合并
     *
     * @param fileName 存储文件名称
     * @param uploadId 上传ID
     * @return /
     */
    @GetMapping("/merge")
    public R merge(String bucketName, String fileName, String uploadId) {
        try {
            Part[] parts = new Part[1000];
            ListPartsResult partResult = minioTemplate.listParts(bucketName, fileName, 1000, 0, uploadId);
            int partNumber = 0;
            for (Part part : partResult.partList()) {
                parts[partNumber] = new Part(partNumber + 1, part.etag());
                partNumber++;
            }

            minioTemplate.completeMultipartUpload(bucketName, fileName, uploadId, parts);

            return R.ok();
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail(500, "文件合并错误");
        }
    }


    @GetMapping("/downloadFile/{fileId}")
    public void downloadFile(@PathVariable("fileId") Long fileId, HttpServletResponse response) throws Exception {
        SysFileEntity sysFileEntity = sysFileService.selectSysFileById(fileId);
        if (ObjectUtils.isNotEmpty(sysFileEntity)) {
//            minioTemplate.downloadFile(minioConfig.getBucketName(), sysFileEntity.getFileNameFull(), sysFileEntity.getFileName(), sysFileEntity.getFileType(), response);
            sysFileService.download(fileId, response);
        } else {
            response.getWriter().write("error");
        }
    }

    @PostMapping("/saveFile")
    public R<SysFileDTO> saveFile(@RequestBody SysFileDTO sysFileDTO) throws Exception {
        SysFileEntity sysFileEntity = SysFileEntity.builder().build();
        BeanUtils.copyProperties(sysFileDTO, sysFileEntity);
        sysFileService.insertSysFile(sysFileEntity);
        sysFileDTO.setId(sysFileEntity.getId());
        return R.ok(sysFileDTO);
    }


    /**
     * 文件上传请求
     */
    @PostMapping("uploadFileByInputStream")
    public R<SysFileVO> uploadFileByInputStream(@RequestParam("fileName") String fileName, @RequestParam("filePath") String filePath, @RequestParam("size") Long size) {
        try {
            // 上传并返回访问地址
            SysFileEntity sysFileEntity = sysFileService.uploadLocalFile(filePath, null, fileName, null, size);
            SysFileVO sysFile = new SysFileVO();
            sysFile.setFileSuffix("." + sysFileEntity.getFileType());
            sysFile.setOssId(String.valueOf(sysFileEntity.getId()));
            sysFile.setName(FileUtils.getName(sysFileEntity.getUrl()));
            sysFile.setUrl(sysFileEntity.getUrl());
            sysFile.setFileSize(sysFileEntity.getFileSize());
            sysFile.setCreateBy(SecurityUtils.getUsername());
            sysFile.setOriginalName(fileName);
            sysFile.setCreateName(SecurityUtils.getNickName());
            sysFile.setCreateTime(new Date());
            return R.ok(sysFile);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            return R.fail(e.getMessage());
        }
    }
}
